#
# Ronin Exploits - A Ruby library for Ronin that provides exploitation and
# payload crafting functionality.
#
# Copyright (c) 2007-2013 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# This file is part of Ronin Exploits.
#
# Ronin is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ronin is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ronin.  If not, see <http://www.gnu.org/licenses/>
#

require 'ronin/exploits/exceptions/target_data_missing'
require 'ronin/exploits/exploit'
require 'ronin/model/has_description'
require 'ronin/model/targets_arch'
require 'ronin/model/targets_os'
require 'ronin/model/targets_software'
require 'ronin/model'

require 'dm-types/yaml'

module Ronin
  module Exploits
    #
    # The {Target} model represents targeting information used for exploits.
    # A target may specify which Architecture, Operating System and
    # Product it targets. The target may also contain additional target
    # parameters.
    #
    class Target

      include Model
      include Model::HasDescription
      include Model::TargetsArch
      include Model::TargetsOS
      include Model::TargetsSoftware

      # Primary key
      property :id, Serial

      # The class-name of the target
      property :type, Discriminator

      # The target parameters to use with the exploit
      property :params, Yaml

      # The exploit the target belongs to
      belongs_to :exploit

      #
      # Creates a new ExploitTarget object
      #
      # @param [Hash] attributes
      #   Additional attributes to create the target with.
      #
      # @yield [target]
      #   If a block is given, it will be passed the new target object.
      #
      # @yieldparam [Target] target
      #   The newly created target object.
      #
      def initialize(attributes={})
        super(attributes)

        yield self if block_given?
      end

      #
      # Searches for target parameter with the matching name.
      #
      # @param [Symbol, String] name
      #   The name to search for.
      #
      # @return [Boolean]
      #   Specifies whether the target contains the parameter with the
      #   matching name.
      #
      def param?(name)
        self.params.has_key?(name.to_sym) unless self.params.nil?
      end

      #
      # Returns the target parameter with the matching name.
      #
      # @param [Symbol, String] name
      #   The name of the target parameter to retrieve.
      #
      # @return [Object, nil]
      #   The target parameter.
      #
      def [](name)
        self.params[name.to_sym] unless self.params.nil?
      end

      #
      # Sets the target parameter with the matching name.
      #
      # @param [Symbol, String] name
      #   The name of the target parameter to set.
      #
      # @param [Object] value
      #   The value to set for the target parameter.
      #
      def []=(name,value)
        self.params = {} if self.params.nil?
        self.params[name.to_sym] = value
      end

      #
      # Determines if the target responds to a method or param.
      #
      # @param [Symbol, String] name
      #   The method name.
      #
      # @param [Boolean] include_private
      #   Specifies whether to include private methods.
      #
      # @return [Boolean]
      #   Specifies whether the target responds to the method or param.
      #
      # @since 1.0.0
      #
      # @api public
      #
      def respond_to?(name,include_private=false)
        super(name,include_private) || param?(name)
      end

      protected

      #
      # Provides transparent access to the target {#params} Hash.
      #
      # @raise [TargetDataMissing]
      #   The target parameter does not exist.
      #
      # @example
      #   target.ip
      #   # => 0xff8025a0
      #
      # @example
      #   target.ip = 0x41414141
      #
      def method_missing(name,*arguments,&block)
        unless block
          name = name.to_s

          if (name[-1..-1] == '=' && arguments.length == 1)
            return self[name.chop] = arguments.first
          elsif arguments.length == 0
            unless param?(name)
              raise(TargetDataMissing,"no such target param #{name.dump}")
            end

            return self[name]
          end
        end

        super(name,*arguments,&block)
      end

    end
  end
end
